/*
The code examples copyrighted by Nokia Corporation that are included to
this material are licensed to you under following MIT-style License:

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Contact: Hannu Mallat <hannu.mallat@nokia.com>

*/
// Test wap push client. NB. the client does not register/deregister
// itself to wappushd over D-BUS, use the scripts in the test
// directory for that.

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/un.h>
#include <getopt.h>
#include <assert.h>
#include <stdarg.h>
#include <syslog.h>
#include <ctype.h>

#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>

#if 0
#include <xml2wbxml/xml2wbxml.h>
#endif

#include <wappushd/wappushd-dbus.h>

static void dump(FILE* f, unsigned char* buf, unsigned int len) {
  unsigned int pos = 0;
  while(pos < len) {
    unsigned int x;
    fprintf(f, "%04x", pos);
    for(x = 0; x < 16; x++) {
      if(pos + x < len) {
	fprintf(f, " %02x", buf[pos + x]);
      } else {
	fprintf(f, "   ");
      }
    }
    fprintf(f, " ");
    for(x = 0; x < 16; x++) {
      if(pos + x < len) {
	fprintf(f, "%c", isprint(buf[pos + x]) ? buf[pos + x] : '.');
      } else {
	fprintf(f, " ");
      }
    }
    fprintf(f, "\n");
    pos += 16;
  }
}

static DBusHandlerResult handle_dbus_message(DBusConnection* c,
					     DBusMessage* msg,
					     void* user_data) {
  unsigned int i;
  const char* msg_iface = dbus_message_get_interface(msg);
  const char* msg_member = dbus_message_get_member(msg);
  const char* msg_sig = dbus_message_get_signature(msg);
  DBusMessageIter iter;
  DBusMessageIter array_iter;
  DBusMessage* reply = NULL;
#if 0
  XML_Char* xml = NULL;
#endif

  fprintf(stdout, "D-Bus message received: %s/%s(%s). \n",
	  msg_iface, msg_member, msg_sig);

  if(!strcmp(msg_iface, WAPPUSH_HANDLER_INTERFACE) &&
     !strcmp(msg_member, WAPPUSH_HANDLER_METHOD) &&
     !strcmp(msg_sig, "yayayqqayay")) {

    unsigned char bearer = ~0;
    unsigned char* src_ptr = NULL;
    int src_ptr_len = 0;
    unsigned char* dst_ptr = NULL;
    int dst_ptr_len = 0;
    unsigned short src_port = 0;
    unsigned short dst_port = 0;
    unsigned char* hdr_ptr = NULL;
    int hdr_ptr_len = 0;
    unsigned char* body_ptr = 0;
    int body_ptr_len = 0;

    dbus_message_iter_init(msg, &iter);

    dbus_message_iter_get_basic(&iter, &bearer);
    dbus_message_iter_next(&iter);

    dbus_message_iter_recurse(&iter, &array_iter);
    dbus_message_iter_get_fixed_array(&array_iter, &src_ptr, &src_ptr_len);
    dbus_message_iter_next(&iter);

    dbus_message_iter_recurse(&iter, &array_iter);
    dbus_message_iter_get_fixed_array(&array_iter, &dst_ptr, &dst_ptr_len);
    dbus_message_iter_next(&iter);

    dbus_message_iter_get_basic(&iter, &src_port);
    dbus_message_iter_next(&iter);

    dbus_message_iter_get_basic(&iter, &dst_port);
    dbus_message_iter_next(&iter);

    dbus_message_iter_recurse(&iter, &array_iter);
    dbus_message_iter_get_fixed_array(&array_iter, &hdr_ptr, &hdr_ptr_len);
    dbus_message_iter_next(&iter);

    dbus_message_iter_recurse(&iter, &array_iter);
    dbus_message_iter_get_fixed_array(&array_iter, &body_ptr, &body_ptr_len);
    dbus_message_iter_next(&iter);

    if(bearer != 0x00) { // TODO: should #define
      fprintf(stdout, "IP push signature received, but bearer is not supported (%d). \n", bearer);
      goto exit;
    }
    fprintf(stdout, "IP push received. \n");

    if(src_ptr_len == 4) {
      fprintf(stdout, "Source IPv4 address: %d.%d.%d.%d:%d\n",
	      src_ptr[0], src_ptr[1], src_ptr[2], src_ptr[3],
	      ntohs(src_port));
    } else if(src_ptr_len == 16) {
      fprintf(stdout, "Source IPv6 address: [");
      for(i = 0; i < 16; i++) {
	fprintf(stdout, "%s%02x", i == 0 ? "" : ":", src_ptr[i]);
      }
      fprintf(stdout, "]:%d\n", ntohs(src_port));
    } else {
      fprintf(stdout, "Unsupported source address length (%d). \n",
	      src_ptr_len);
    }

    if(dst_ptr_len == 4) {
      fprintf(stdout, "Destination IPv4 address: %d.%d.%d.%d:%d\n",
	      dst_ptr[0], dst_ptr[1], dst_ptr[2], dst_ptr[3],
	      ntohs(dst_port));
    } else if(dst_ptr_len == 16) {
      fprintf(stdout, "Destination IPv6 address: [");
      for(i = 0; i < 16; i++) {
	fprintf(stdout, "%s%02x", i == 0 ? "" : ":", dst_ptr[i]);
      }
      fprintf(stdout, "]:%d\n",
	      ntohs(dst_port));
    } else {
      fprintf(stdout, "Unsupported source address length (%d). \n",
	      dst_ptr_len);
    }

    fprintf(stdout, "WSP headers: \n");
    dump(stdout, hdr_ptr, hdr_ptr_len);

    fprintf(stdout, "WSP payload: \n");
    dump(stdout, body_ptr, body_ptr_len);

#if 0
    if(wbxml2xml(body_ptr, body_ptr_len, &xml, 1) == 0) {
      fprintf(stdout, "Decoded XML: \n%s\n", xml);
      free(xml);
    }
#endif

  } else if(!strcmp(msg_iface, WAPPUSH_HANDLER_INTERFACE) &&
	    !strcmp(msg_member, WAPPUSH_HANDLER_METHOD) &&
     !strcmp(msg_sig, "ysiiayay")) {

    DBusMessageIter iter;
    DBusMessageIter array_iter;
    DBusMessage* reply = NULL;
    unsigned char bearer = ~0;
    const char* src = NULL;
    int src_port = 0;
    int dst_port = 0;
    unsigned char* hdr_ptr = NULL;
    int hdr_ptr_len = 0;
    unsigned char* body_ptr = 0;
    int body_ptr_len = 0;

    dbus_message_iter_init(msg, &iter);

    dbus_message_iter_get_basic(&iter, &bearer);
    dbus_message_iter_next(&iter);

    dbus_message_iter_get_basic(&iter, &src);
    dbus_message_iter_next(&iter);

    dbus_message_iter_get_basic(&iter, &src_port);
    dbus_message_iter_next(&iter);

    dbus_message_iter_get_basic(&iter, &dst_port);
    dbus_message_iter_next(&iter);

    dbus_message_iter_recurse(&iter, &array_iter);
    dbus_message_iter_get_fixed_array(&array_iter, &hdr_ptr, &hdr_ptr_len);
    dbus_message_iter_next(&iter);

    dbus_message_iter_recurse(&iter, &array_iter);
    dbus_message_iter_get_fixed_array(&array_iter, &body_ptr, &body_ptr_len);
    dbus_message_iter_next(&iter);

    if(bearer != 0x01) { // TODO: should #define
      fprintf(stdout, "SMS push signature received, but bearer is not supported (%d). \n", bearer);
      goto exit;
    }
    fprintf(stdout, "SMS push received. \n");

    fprintf(stdout, "Source address: `%s'\n", src);

    fprintf(stdout, "Source port: %d\n", src_port);

    fprintf(stdout, "Destination port: %d\n", dst_port);

    fprintf(stdout, "WSP headers: \n");
    dump(stdout, hdr_ptr, hdr_ptr_len);

    fprintf(stdout, "WSP payload: \n");
    dump(stdout, body_ptr, body_ptr_len);

#if 0
    if(wbxml2xml(body_ptr, body_ptr_len, &xml, 1) == 0) {
      fprintf(stdout, "Decoded XML: \n%s\n", xml);
      free(xml);
    }
#endif

  } else {

    fprintf(stderr, "Unknown push signature received. \n");
    goto exit;

  }

 exit:
  reply = dbus_message_new_method_return(msg);

  if(reply != NULL) {
    if((!dbus_message_get_no_reply(msg)) ||
       dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_CALL) {
      dbus_connection_send(c, reply, NULL);
    }
    dbus_message_unref(reply);
  }



  return DBUS_HANDLER_RESULT_HANDLED;
}

static GMainLoop* glib_main_loop = 0;


static void leave(int sig){
  if(glib_main_loop){
    g_main_loop_quit(glib_main_loop);
  }
}


int main(int argc, char* argv[]) {
  static struct DBusObjectPathVTable dbus_vtable = {
    NULL, handle_dbus_message
  };
  DBusError dbus_error;
  DBusConnection* dbus;
  

  dbus_error_init(&dbus_error);

  dbus = dbus_bus_get(DBUS_BUS_SYSTEM, &dbus_error);
  if(dbus == NULL) {
    fprintf(stderr, "Failed to connect to system DBus: '%s'. \n", dbus_error.message);
    goto exit;
  }
  dbus_connection_set_exit_on_disconnect(dbus, FALSE);
  dbus_connection_setup_with_g_main(dbus, NULL);
  if(dbus_connection_register_object_path(dbus,
					  "/com/nokia/dm",
					  &dbus_vtable,
					  NULL) == FALSE) {
    fprintf(stderr, "Failed to register DBus message handler. \n");
    goto exit;
  }

  if(dbus_bus_request_name(dbus,
			   "com.nokia.dm",
			   DBUS_NAME_FLAG_REPLACE_EXISTING,
			   &dbus_error) != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
    fprintf(stderr, "Unable to acquire DBus service: '%s'. \n", dbus_error.message);
    goto exit;
  }

  if((glib_main_loop = g_main_loop_new(NULL, FALSE)) == NULL) {
    fprintf(stderr, "Failed to set up main loop, exiting. \n");
    goto exit;
  }

  signal(SIGINT, &leave);

  g_main_loop_run(glib_main_loop);
  g_main_loop_unref(glib_main_loop);

exit:

  exit(0);
}
